
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/slab.h>


MODULE_AUTHOR("mdy");
MODULE_DESCRIPTION("demo");
MODULE_LICENSE("GPL");

static dev_t devno;
static struct class* second_char_cls;

struct second_char_dev
{
    struct cdev c_dev;
    atomic_t counter;
    struct timer_list s_timer;
};

static struct second_char_dev* second_devp;

static void second_timer_handler(struct timer_list *t)
{
    struct second_char_dev* pdev = from_timer(pdev, t, s_timer);

    mod_timer(&pdev->s_timer, jiffies + HZ);
    atomic_inc(&pdev->counter);

    printk(KERN_INFO "current jiffies is %ld\n", jiffies);
}

static int second_open(struct inode* inode, struct file* filep)
{
    timer_setup(&second_devp->s_timer, second_timer_handler, 0);
    second_devp->s_timer.expires = jiffies + HZ;
    add_timer(&second_devp->s_timer);
    atomic_set(&second_devp->counter, 0);

    return 0;
}

static int second_release(struct inode* inode, struct file* filep)
{
    del_timer(&second_devp->s_timer);

    return 0;
}

static ssize_t second_read(struct file* filp, char __user* buf, size_t count, loff_t* ppos)
{
    int counter;

    counter = atomic_read(&second_devp->counter);
    if (put_user(counter, (int*)buf))
    {
        return -EFAULT;
    }
    else
    {
        return sizeof(unsigned int);
    }
}

static const struct file_operations second_fops =
{
    .owner = THIS_MODULE,
    .open = second_open,
    .release = second_release,
    .read = second_read,
};

static void second_setup_cdev(struct second_char_dev* pdev, int index)
{
    int err;
    struct device* second_char_device;

    cdev_init(&pdev->c_dev, &second_fops);
    pdev->c_dev.owner = THIS_MODULE;
    err = cdev_add(&pdev->c_dev, devno, 1);
    if (0 != err)
    {
        printk(KERN_ERR "Failed to add second device\n");
    }

    second_char_cls = class_create(THIS_MODULE, "second");
    second_char_device = device_create(second_char_cls, NULL, devno, NULL, "second");
}

static int __init second_demo_init(void)
{
    int ret = 0;

    alloc_chrdev_region(&devno, 0, 1, "second");
    if (ret < 0)
    {
        return ret;
    }

    second_devp = kzalloc(sizeof(*second_devp), GFP_KERNEL);
    if (NULL != second_devp)
    {
        second_setup_cdev(second_devp, 0);
    }
    else
    {
        ret = -ENOMEM;
        unregister_chrdev_region(devno, 1);
    }

    return ret;
}

static void __exit second_demo_exit(void)
{
    device_destroy(second_char_cls, devno);
    class_destroy(second_char_cls);
    cdev_del(&second_devp->c_dev);
    kfree(second_devp);
    unregister_chrdev_region(devno, 1);

    return;
}

module_init(second_demo_init);
module_exit(second_demo_exit);

